import {
  ModelError,
  RuntimeError,
  RuntimeModelError,
} from '@ibiz-template/core';
import {
  execDELogicById,
  IPanelDataContainerController,
  IViewController,
} from '@ibiz-template/runtime';
import { IPanelContainer } from '@ibiz/model-core';
import { PanelItemController } from '../../control';
import { SingleDataContainerState } from './single-data-container.state';

/**
 * 单项数据容器控制器
 *
 * @export
 * @class SingleDataContainerController
 * @extends {PanelItemController}
 */
export class SingleDataContainerController
  extends PanelItemController<IPanelContainer>
  implements IPanelDataContainerController
{
  declare state: SingleDataContainerState;

  readonly isDataContainer = true;

  /**
   * 单项数据容器，根据数据模式
   * @author lxm
   * @date 2023-07-14 10:59:02
   * @readonly
   * @type {IData}
   */
  get data(): IData {
    return this.state.data;
  }

  protected createState(): SingleDataContainerState {
    return new SingleDataContainerState(this.parent?.state);
  }

  protected async onInit(): Promise<void> {
    await super.onInit();
    this.initContainerData();
  }

  /**
   * 根据来源类型初始化容器数据
   * @author lxm
   * @date 2023-08-04 03:05:59
   * @protected
   */
  protected initContainerData(): void {
    const { dataSourceType, dataName } = this.model;
    switch (dataSourceType) {
      case 'DEACTION':
      case 'DEDATASET':
        this.setDataByDeMethod();
        break;
      case 'APPGLOBALPARAM':
        this.setDataByAppGlobalParam();
        break;
      case 'DELOGIC':
        this.setDataByDeLogic();
        break;
      case 'TOPVIEWSESSIONPARAM': {
        if (!dataName) {
          throw new RuntimeModelError(this.model, '没有配置数据对象名称');
        }
        this.bindViewData(this.panel.getTopView(), dataName);
        break;
      }
      case 'VIEWSESSIONPARAM': {
        if (!dataName) {
          throw new RuntimeModelError(this.model, '没有配置数据对象名称');
        }
        this.bindViewData(this.panel.view, dataName);
        break;
      }
      default:
        throw new ModelError(this.model, `数据源类型${dataSourceType}暂未支持`);
    }
  }

  /**
   * 通过实体设置视图逻辑
   * @author lxm
   * @date 2023-08-04 03:00:31
   * @protected
   * @return {*}  {Promise<void>}
   */
  protected async setDataByDeLogic(): Promise<void> {
    const { appDataEntityId, appDELogicId } = this.model;
    if (!appDELogicId) {
      throw new RuntimeModelError(this.model, '没有配置实体逻辑');
    }
    if (!appDataEntityId) {
      throw new RuntimeModelError(this.model, '没有配置实体');
    }

    const data = await execDELogicById(
      appDELogicId,
      appDataEntityId,
      this.panel.context,
      [],
      this.panel.params,
    );

    if (!data) {
      throw new RuntimeError(`实体逻辑${appDELogicId}没有返回值`);
    }

    this.state.data = data as IData;
  }

  /**
   * 设置全局变量为当前容器数据
   * @author lxm
   * @date 2023-08-04 01:55:07
   * @protected
   */
  protected setDataByAppGlobalParam(): void {
    const { dataName } = this.model;
    const originData = dataName ? ibiz.appData![dataName] : ibiz.appData!;
    if (originData) {
      this.state.data = originData;
    } else {
      ibiz.log.error(`全局变量里没有${dataName}属性`);
    }
  }

  /**
   * 请求实体行为并把返回值设置为当前容器的数据
   * @author lxm
   * @date 2023-08-04 11:47:17
   * @protected
   * @return {*}  {Promise<void>}
   */
  protected async setDataByDeMethod(): Promise<void> {
    const { appDEMethodId, appDataEntityId } = this.model;
    if (!appDEMethodId) {
      throw new RuntimeModelError(this.model, '没有配置实体行为');
    }
    if (!appDataEntityId) {
      throw new RuntimeModelError(this.model, '没有配置实体');
    }
    const app = ibiz.hub.getApp(this.panel.context.srfappid);
    const res = await app.deService.exec(
      appDataEntityId,
      appDEMethodId,
      this.panel.context,
      [],
      this.panel.params,
    );
    if (res.ok && res.data) {
      this.state.data = res.data;
    }
  }

  /**
   * 绑定指定视图会话的变量
   * @author lxm
   * @date 2023-07-14 02:03:56
   * @protected
   * @param {IViewController} view 绑定视图控制器
   * @param {string} dataName 变量名称
   */
  protected bindViewData(view: IViewController, dataName: string): void {
    if (!Object.prototype.hasOwnProperty.call(view.state, dataName)) {
      ibiz.log.error(`绑定视图的会话不存在${dataName}`);
      return;
    }
    const updateData = (): void => {
      this.state.data = (view.state as IData)[dataName];
    };
    updateData(); // 先绑定一次

    // 每次视图数据变更之后更新一下
    view.evt.on('onDataChange', () => {
      updateData();
    });
  }
}
